package cn.tedu._07springaop.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * @Aspect注解
 * 1. 表示当前类为一个切面类
 * 2.当调用目标方法add sub等 会先检查是否定义了切面类
 * 3.如果定义了切面类，则调用切面类中的方法
 */
@Aspect
@Component
public class LogAspect {
    //1.拦截指定类的所有方法
   @Pointcut(value="execution(public int cn.tedu._07springaop.aop.CalculatorImpl.*(..))")
    //2.拦截所有类的所有方法
//    @Pointcut(value = "execution(* * ..*.*(..))")
    //3.拦截指定类的1个方法
//@Pointcut(value="execution(public int cn.tedu._07springaop.aop.CalculatorImpl.add(..))")
    //4.拦截指定类的多个方法 使用||连接符
//@Pointcut(value="execution(public int cn.tedu._07springaop.aop.CalculatorImpl.add(..))" +
//        " || execution(public int cn.tedu._07springaop.aop.CalculatorImpl.sub(..))")
//5.粗粒度：bean("") IoC容器中Bean对象的名称;
//    @Pointcut(value="bean(calculatorImpl)")
//6.粗粒度：with("包名.类名")
//    @Pointcut(value="within(cn.tedu._07springaop.aop.CalculatorImpl)")
    public void doTime(){}




    /**
     * @Before(value = "execution()")
     * 前置通知 调用目标方法之前执行
     * value参数 切入点表达式 表示哪些方法需要被植入增强逻辑
     *      */
    @Before(value ="doTime()")
    public void beforeMethod(JoinPoint joinPoint) {
        String name=joinPoint.getSignature().getName();
        String args= Arrays.toString(joinPoint.getArgs());
        System.out.println("前置"+name+"方法开始 参数为"+args);
    }
    /**
     * @After(value = "execution()")
     * 1.后置通知 调用目标方法彻底结束[正常结束][异常结束]之后执行
     * 2.没有权限获取目标方法返回值
     * value参数 切入点表达式 表示哪些方法需要被植入增强逻辑
     * */
    @After(value = "doTime()")
    public void afterMethod(JoinPoint joinPoint) {
        String name=joinPoint.getSignature().getName();
        System.out.println("后置"+name+"方法结束");

    }
    /**
     * AfterReturning注解 返回通知
     * 1.目标方法正常结束之后执行
     * 2.返回通知有权限获取目标方法返回值
     */
    @AfterReturning(value = "doTime()", returning = "result")
    public void afterReturning(JoinPoint joinPoint,Object result){
        String name=joinPoint.getSignature().getName();
        System.out.println(name+"方法正常结束，返回值是："+result);

    }
    /**
     * AfterThrowing注解: 异常通知
     *   1.只在目标方法抛出异常之后执行;
     *   2.异常通知有权限获取到目标方法抛出的异常对象;
     */
    @AfterThrowing(value = "doTime()", throwing = "ex")
    public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex){
        String name = joinPoint.getSignature().getName();
        System.out.println("[异常]" + name + "方法抛出异常,异常信息为:" + ex.getMessage());
    }

    /**
     * Around注解: 环绕通知
     *   1.等价于 @Before + @After + @AfterReturning + @AfterThrowing 的组合;
     *   2.环绕通知有权限访问目标方法的返回值;
     *   3.环绕通知有权限获取异常对象[目标方法抛出异常时];
     *   4.通常使用 try ... catch ... finally ...结果包裹;
     */
    @Around(value = "doTime()")
    public Object aroundMethod(ProceedingJoinPoint joinPoint){
        Object result = null;

        try {
            System.out.println("[环绕通知-前置通知]");
            result = joinPoint.proceed();
            System.out.println("[环绕通知-返回通知]");
        } catch (Throwable e) {
            System.out.println("[环绕通知-异常通知]");
        } finally {
            System.out.println("[环绕通知-后置通知]");
        }

        return result;
    }
}
